/* drivers/input/keyboard/goldfish-events.c
**
** Copyright (C) 2007 Google, Inc.
**
** This software is licensed under the terms of the GNU General Public
** License version 2, as published by the Free Software Foundation, and
** may be copied, distributed, and modified under those terms.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU General Public License for more details.
**
*/

//#include <xen/config.h>
//#include <asm-arm/irq.h>
//#include <goldfish/head.h>	
#include <asm-arm/io.h>
#include <asm-arm/errno-base.h>
#include <goldfish/goldfish_events.h>
#include <asm-arm/mm.h>
#include <xen/softirq.h>
#include <goldfish/audio.h>
#include <xen/sched.h>
#include <asm-arm/current.h>
#include <xen/event.h>
#include <public/goldfish_drivers_types.h>
#include <xen/foreground.h>
#include <goldfish/dma.h>
#include <goldfish/goldfishfb.h>
enum {
	REG_READ        = 0x00,
	REG_SET_PAGE    = 0x00,
	REG_LEN         = 0x04,
	REG_DATA        = 0x08,

	PAGE_NAME       = 0x00000,
	PAGE_EVBITS     = 0x10000,
	PAGE_ABSDATA    = 0x20000 | EV_ABS,
};

struct event_dev {
    struct input_dev *input;
    int irq;
    unsigned addr;
    char name[0];
};

char keyboard[] = {'\0','\0','1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '\0','\0','\b','\0',
                   'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '\0','\0','\n','\0',
                   'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', '\0','\0','\0','\0',
                   '\0','z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', '\0','\0','\0',' '};


struct console cons;

/**
 * input_event() - report new input event
 * @dev: device that generated the event
 * @type: type of the event
 * @code: event code
 * @value: value of the event
 *
 * This function should be used by drivers implementing various input
 * devices. See also input_inject_event().
 */

/*void input_event(struct input_dev *dev,
		 unsigned int type, unsigned int code, int value)
{
	unsigned long flags;

	if (is_event_supported(type, dev->evbit, EV_MAX)) {

		spin_lock_irqsave(&dev->event_lock, flags);
		//modify add_input_randomness(type, code, value);
		input_handle_event(dev, type, code, value);
		spin_unlock_irqrestore(&dev->event_lock, flags);
	}
}*/
 #define CAPSON 1
 #define CAPSOFF 0
#define POWER 116//158
#define SWITCH 158
#define KEYBOARD 1
#define MICE 3
#define rotate 5
static int nr = 0,index = 0;
extern unsigned int foreground_domain_copy;
struct event_info switch_info[10] = {{3,0,125},{3,1,5},{3,2,0},{1,330,1},{0,0,0},{3,0,125},{3,1,5},{3,2,0},{1,330,0},{0,0,0}};
struct event_info event_info_tmp[10];
int events_interrupt(int irq, void *dev_id,struct cpu_user_regs *regs)
{
    struct event_dev *edev = dev_id;
    unsigned type, code, value;
    unsigned long flags;
    static int flag = CAPSOFF;
    static int mute = 0;
	
	static int esc_status = 0;//situation return to xen
	//static int switch_status = 0;//situation switch between domain
	static s64 time_ellapse = 0;
	//static s64 time_ellapse_switch = 0;
	struct domain* d = find_domain_by_domid(foreground_domain);
	struct domain* d_back = find_domain_by_domid((foreground_domain+1)%2);
	struct domain* dom0 = find_domain_by_domid(0);
	struct domain* dom1 = find_domain_by_domid(1);
	
    type = __raw_readl(edev->addr + REG_READ);
    code = __raw_readl(edev->addr + REG_READ);
    value = __raw_readl(edev->addr + REG_READ);
    
    if(type == 1 && code == 56 && value == 0)//left alt
             flag = flag?CAPSOFF:CAPSON;
    /*if(type == 1 && code == 116 && value == 0)//power
     {
               mute = mute?0:1;
               goldfish_audio_fops.ioctl(NULL,NULL,mute,0);
     }*/

        //printk("code=%d\n", code);        
    if(code == POWER /*&& d != NULL*/)//return to xen
    {
		if(esc_status == 1 && time_ellapse && NOW()- time_ellapse > 3000000000ULL )
		{
			raise_softirq(CONSOLE_SOFTIRQ);
			//*(d->status) = 101;
			//send_guest_virq(d->vcpu[0], VIRQ_FB);
			foreground_domain_copy = foreground_domain;
			foreground_domain = MAX_DOMAIN_NUM;
            //cons.wpos = 0;
		}
		else if(esc_status == 1 && dom0 && dom1 && !test_bit(_DOMF_ctrl_pause, &d_back->domain_flags))
		{
			foreground_domain = (foreground_domain + 1)%2 ;
                        send_guest_virq(dom0->vcpu[0], VIRQ_SWITCH);
                        if(foreground_domain){
				/*goldfishfb->fb.screen_base = fbdom_dmabase;
				goldfishfb->fb.fix.smem_start = fbdom_paddr;
				__raw_writel(2, goldfishfb->reg_base + 0xc);
				goldfish_fb_pan_display(&goldfishfb->fb.var, &goldfishfb->fb);*/
                                testfb_dom();
			}
			else if(dom0 && dom0->scan_code){
				//memcpy((void*)(dom0->scan_code), (const void*)switch_info, 10*sizeof(struct event_info));
				memcpy(/*(void*)d->scan_code*/(void*)dom0->shared_info + 0xd00, (const void*)switch_info, 10*sizeof(struct event_info));
				send_guest_virq(dom0->vcpu[0], VIRQ_KEYBOARD);
			}
		}
		if(value == 1)
		{
			esc_status = 1;
			time_ellapse = NOW();
		}
		else
		{
			esc_status = 0;
			time_ellapse = 0;
		}
    }
    /*else if(code == SWITCH && d != NULL)//switch between domain
    {
		if(switch_status == 1 && time_ellapse_switch && NOW()- time_ellapse_switch > 3000000000ULL )
                {
                        //send_guest_virq(d->vcpu[0], VIRQ_FB);
                        foreground_domain = (foreground_domain + 1)%2 ;
			send_guest_virq(dom0->vcpu[0], VIRQ_SWITCH);
			if(foreground_domain)
				testfb();
            //cons.wpos = 0;
                }
                if(value == 1)
                {
                        switch_status = 1;
                        time_ellapse_switch = NOW();
                }
                else
                {
                        switch_status = 0;
                        time_ellapse_switch = 0;
                }
    }*/	
    else if(foreground_domain == MAX_DOMAIN_NUM)
    {
        if(value == 0 && type == KEYBOARD && code < sizeof(keyboard))
              cons.buf[cons.wpos++] = keyboard[code];
        if(cons.wpos == CONSBUFSIZE)
              cons.wpos = 0;
    }    
//		printk("type:%d\tcode:%d\tvalue:%d\n",type,code,value);
	//code represents keycode
	//value represents the status of the key (press_up or press_down)
   //if(current->virq_to_evtchn[VIRQ_KEYBOARD] != 0 && current->domain->scan_code )
   if(d != NULL && d->vcpu[0]->virq_to_evtchn[VIRQ_KEYBOARD] != 0 /*&& d->scan_code*/)
   {
		  //struct event_info * info = (struct event_info *)d->scan_code;
		struct event_info * info;	
		if( nr == 0)
		{
			  //keyboard event or scroll button event on mouse
			if(type == KEYBOARD)
				nr = 2;
			//mouse event	 
			if(type == MICE)
				nr = 5;
			if(type == rotate)
				nr =1;
			index = 0;
		}
		if(index < nr)
		{
			//	printk("type:%d\tcode:%d\tvalue:%d,index=%d\n",type,code,value,index);
			    //info = (struct event_info *)((u32)info+index++*sizeof(struct event_info));
			info = event_info_tmp + index++;
			info->code = code;
	   		info->value = value;
	   		info->type = type;
		}
		if(index && index == nr && d->domain_id == 0)
	   	{//printk("send event to domain%d domid=%d\n", foreground_domain, d->domain_id);
			//local_irq_save(flags);
			//write_ptbase(d->vcpu[0]);//printk("VIRQ_KEYBOARD\n");
			if(!virq_mask(d->vcpu[0], VIRQ_KEYBOARD))
			{
//printk("event_info_tmp\n");
				memcpy(/*(void*)d->scan_code*/(void*)d->shared_info + 0xd00, (const void*)event_info_tmp, nr*sizeof(struct event_info));
				send_guest_virq(d->vcpu[0], VIRQ_KEYBOARD);
			}
			nr = index = 0;
			//write_ptbase(current);
			//local_irq_restore(flags);
		}
		else if(index && index == nr && d->domain_id == 1 && nr == 2)//send keyboard signal to dom1
		{
			if(!virq_mask(d->vcpu[0], VIRQ_KEYBOARD)){
                                memcpy(/*(void*)d->scan_code*/(void*)d->shared_info + 0xd00, (const void*)event_info_tmp, nr*sizeof(struct event_info));
                                send_guest_virq(d->vcpu[0], VIRQ_KEYBOARD);
                        }
                        nr = index = 0;
		}
		else if(index && index == nr && d->domain_id == 1)
		{
			nr = index = 0;
		}
    }
	/*else if(index < nr && current->virq_to_evtchn[VIRQ_KEYBOARD] != 0 )
	{
		   struct event_info * info = (struct event_info *)current->domain->scan_code;
			    info = (struct event_info *)((u32)info+index++*sizeof(struct event_info));
				info->code = code;
		   		info->value = value;
		   		info->type = type;
		   if(index == nr)
	   		{
				nr = index = 0;
				send_guest_virq(current, VIRQ_KEYBOARD);
			}
	}*/
	else
		nr = index = 0;

	input_event(edev->input, type, code, value);
    return IRQ_HANDLED;
}

static void events_import_bits(struct event_dev *edev, unsigned long bits[], unsigned type, size_t count)
{
	int i, j;
	size_t size;
	uint8_t val;
	unsigned addr = edev->addr;
	__raw_writel(PAGE_EVBITS | type, addr + REG_SET_PAGE);
	size = __raw_readl(addr + REG_LEN) * 8;
	if (size < count)
		count = size;
	addr = addr + REG_DATA;
	for (i = 0; i < count; i += 8) {
		val = __raw_readb(addr++);
		for (j = 0; j < 8; j++)
			if(val & 1 << j)
				set_bit(i + j, bits);
	}
}

//void map_alloc(unsigned long first_page, unsigned long nr_pages);

void *__arm_ioremap(unsigned long phys_addr, size_t size,
			    unsigned int mtype)
{
	//struct page_info *pg = phys_to_page(phys_addr);
	//unsigned int order = get_order_from_bytes(size);
	//map_alloc(page_to_pfn(pg), 1 << order);
	//return (void *)page_to_virt(pg);
	return (void *)(phys_addr - 0x0F000000);
}

//#define ioremap(cookie,size)		__arm_ioremap(cookie, size, MT_DEVICE)

/**
 * platform_get_irq - get an IRQ for a device
 * @dev: platform device
 * @num: IRQ number index
 */
int platform_get_irq(struct platform_device *dev, unsigned int num);
/*{
	struct resource *r = platform_get_resource(dev, IORESOURCE_IRQ, num);

	return r ? r->start : -ENXIO;
}*/

static inline void input_set_abs_params(struct input_dev *dev, int axis, int min, int max, int fuzz, int flat)
{
	dev->absmin[axis] = min;
	dev->absmax[axis] = max;
	dev->absfuzz[axis] = fuzz;
	dev->absflat[axis] = flat;

	dev->absbit[BIT_WORD(axis)] |= BIT_MASK(axis);
}

struct input_dev *input_dev = NULL;

static int events_probe(struct platform_device *pdev)
{
    struct event_dev *edev = NULL;
    struct resource *res;
    unsigned keymapnamelen;
    int i;
    int count;
    int irq;
    unsigned addr;
    int ret;
    
    printk("*** events probe ***\n");

    input_dev = input_allocate_device();
    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	printk("res_start:%x,res_end:%x\n",res->start,res->end);
    if(!input_dev || !res) goto fail;

    addr = (unsigned) ioremap(res->start, 4096);
    irq = platform_get_irq(pdev, 0);

    //printk("events_probe() addr=0x%08x irq=%d\n", addr, irq);

    if(!addr) goto fail;
    if(irq < 0) goto fail;

    __raw_writel(PAGE_NAME, addr + REG_SET_PAGE);
    keymapnamelen = __raw_readl(addr + REG_LEN);

    //edev = _xmalloc(sizeof(struct event_dev) + keymapnamelen + 1, 1);
	//printk("size = %x\n",keymapnamelen);
	edev = xmalloc_bytes(sizeof(struct event_dev) + keymapnamelen + 1);
    if (!edev) goto fail;

    edev->input = input_dev;
    edev->addr = addr;
    edev->irq = irq;
    
    for (i = 0; i < keymapnamelen; i++) {
        edev->name[i] = __raw_readb(edev->addr + REG_DATA + i);
    }
    printk("events_probe() keymap=%s\n", edev->name);

    events_import_bits(edev, input_dev->evbit, EV_SYN, EV_MAX);
    events_import_bits(edev, input_dev->keybit, EV_KEY, KEY_MAX);
    events_import_bits(edev, input_dev->relbit, EV_REL, REL_MAX);
    events_import_bits(edev, input_dev->absbit, EV_ABS, ABS_MAX);
    events_import_bits(edev, input_dev->mscbit, EV_MSC, MSC_MAX);
    events_import_bits(edev, input_dev->ledbit, EV_LED, LED_MAX);
    events_import_bits(edev, input_dev->sndbit, EV_SND, SND_MAX);
    events_import_bits(edev, input_dev->ffbit, EV_FF, FF_MAX);
    events_import_bits(edev, input_dev->swbit, EV_SW, SW_MAX);

    __raw_writel(PAGE_ABSDATA, addr + REG_SET_PAGE);
    count = __raw_readl(addr + REG_LEN) / (4 * 4);
    if (count > ABS_MAX)
        count = ABS_MAX;
    for(i = 0; i < count; i++) {
        int val[4]; 
        int j;
        if (!test_bit(i, input_dev->absbit))
            continue;
        for(j = 0; j < ARRAY_SIZE(val); j++)
            val[j] = __raw_readl(edev->addr + REG_DATA + (i * ARRAY_SIZE(val) + j) * 4);
        input_set_abs_params(input_dev, i, val[0], val[1], val[2], val[3]);
    }
    
    //modify platform_set_drvdata(pdev, edev);

    input_dev->name = edev->name;
    //modify input_set_drvdata(input_dev, edev);
    
    //modify ret = input_register_device(input_dev);
    //modify if (ret)
    //modify     goto fail;

    if(request_irq(edev->irq, events_interrupt, 0,
                   "goldfish-events-keypad", edev) < 0) {
        //modify input_unregister_device(input_dev);
        xfree(edev);
        return -EINVAL;
    }
	printk("event probe success\n");
    return 0;

fail:
    xfree(edev);
    //modify input_free_device(input_dev);
   printk("event probe Failed\n"); 
    return -EINVAL;
}

struct platform_driver events_driver = {
    .probe = events_probe,
    //.driver = {
    //    .name = "goldfish_events",
    //},
};

//modify module_init(events_init);
//modify module_exit(events_exit);

//modify MODULE_AUTHOR("Brian Swetland");
//modify MODULE_DESCRIPTION("Goldfish Event Device");
//modify MODULE_LICENSE("GPL");
